// (C) 2022 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.

`timescale 1ns / 1ps

module Ocp_ctrl
(
input  iClk,
input  iRst_n,
input  i_1mSCE,
input  i_20mSCE,


input  i_OCP_CARD_PRSNTB_N,
input  iPSU_PWR_OK,
input  i_PWRGD_NIC_PWR,


output o_FM_OCP_AUX_PWR_EN,                     //enablement of aux power
output o_FM_OCP_MAIN_PWR_EN,                    //enablement of main power
output o_FM_PLD_OCP_RBT_ISOLATE_N,              //isolate board and ocp card communication when power not steady
output o_PWRGD_NIC_PWR_DLY,                     //indicate NIC power up sequence done
output o_OcpPwrFlt_LED                          //assert 1s after NIC POWER GOOD
);

////////////////////////////////////////////////////////////////////////////////// //
// Parameters                                                                      //
////////////////////////////////////////////////////////////////////////////////// //

////////////////////////////////////////////////////////////////////////////////// //
localparam  LOW  = 1'b0;
localparam  HIGH = 1'b1;

////////////////////////////////////////////////////////////////////////////////// //
// VariableS Declaration                                                           //
////////////////////////////////////////////////////////////////////////////////// //
   reg  [2:0]rstate;
   localparam  ST_INIT                    = 3'd0;
   localparam  ST_ID_MODE                 = 3'd1;
   localparam  ST_AUX_MODE                = 3'd2;
   localparam  ST_MAIN_MODE               = 3'd3;
   localparam	ST_PROGRAMMING_MODE	   	= 3'd4;
   localparam  ST_PWR_DOWN                = 3'd5;

   
   reg   r_FM_OCP_AUX_PWR_EN;
   reg   r_FM_OCP_MAIN_PWR_EN;
   wire  w_ocp_id_en   ;
   wire  w_ocp_aux_en   ;
   wire  w_ocp_main_en  ;
   wire  w_ocp_programming_en;
   wire  w_ocp_int;

   wire waux_timeout;
   wire wmain_timeout;


	assign w_ocp_int            =   iRst_n & !i_OCP_CARD_PRSNTB_N;			//indicate OCP have P12V and P3V3, with card present
	assign w_ocp_programming_en =   !r_FM_OCP_AUX_PWR_EN & r_FM_OCP_MAIN_PWR_EN	;	//if high enter programming mode
	assign w_ocp_aux_en         =	   w_ocp_int &!iPSU_PWR_OK;								//if high enter Aux power mode
   assign w_ocp_main_en        =	   w_ocp_int & iPSU_PWR_OK;								//if high enter Main power mode
   assign w_ocp_id_en          =    w_ocp_int & !r_FM_OCP_AUX_PWR_EN & !r_FM_OCP_MAIN_PWR_EN;	// if high enter id mode
 
 
    assign o_FM_OCP_AUX_PWR_EN = r_FM_OCP_AUX_PWR_EN;
    assign o_FM_OCP_MAIN_PWR_EN = r_FM_OCP_MAIN_PWR_EN;

////////////////////////////////////////////////////////////////////////////////// //
// state machine for power sequence logic                                                           
////////////////////////////////////////////////////////////////////////////////// //
always@(posedge iClk or negedge iRst_n)
begin
    if(!iRst_n)
    begin
        rstate                <= ST_INIT;
        r_FM_OCP_AUX_PWR_EN    <= LOW;
        r_FM_OCP_MAIN_PWR_EN   <= LOW;
        
    end

  else begin  
 case(rstate)

    ST_INIT:
    begin
    	  r_FM_OCP_AUX_PWR_EN    <= LOW;
        r_FM_OCP_MAIN_PWR_EN   <= LOW;
		if(w_ocp_id_en)
		begin
	 		rstate 				    <= ST_ID_MODE;
		end

    end
    ST_ID_MODE:
    begin
    	  r_FM_OCP_AUX_PWR_EN      <= LOW;
        r_FM_OCP_MAIN_PWR_EN     <= LOW;
		if(w_ocp_aux_en)
		begin
	 		rstate 				     <= ST_AUX_MODE;
			r_FM_OCP_AUX_PWR_EN    <= HIGH;
         r_FM_OCP_MAIN_PWR_EN   <= LOW;
		end

		else if(w_ocp_programming_en)
		begin
			 rstate      	      	<= ST_PROGRAMMING_MODE;
			 r_FM_OCP_AUX_PWR_EN    <= LOW;
          r_FM_OCP_MAIN_PWR_EN   <= HIGH;
		end

		else if (!w_ocp_int)
        begin 
        	rstate  <= ST_INIT;
		  r_FM_OCP_AUX_PWR_EN    <= LOW;
        r_FM_OCP_MAIN_PWR_EN   <= LOW;
        end
    end

    ST_PROGRAMMING_MODE:
    begin
    	  r_FM_OCP_AUX_PWR_EN    <= LOW;
        r_FM_OCP_MAIN_PWR_EN   <= HIGH;
        if(w_ocp_id_en)
        begin
        	 rstate                   <= ST_ID_MODE;
			 r_FM_OCP_AUX_PWR_EN      <= LOW;
          r_FM_OCP_MAIN_PWR_EN     <= LOW;
        end

        else if (!w_ocp_int)
        begin 
        	rstate  <= ST_INIT;
		   r_FM_OCP_AUX_PWR_EN      <= LOW;
         r_FM_OCP_MAIN_PWR_EN     <= LOW;
        end
    end

    ST_AUX_MODE:
    begin
    	  r_FM_OCP_AUX_PWR_EN    <= HIGH;
        r_FM_OCP_MAIN_PWR_EN   <= LOW;
		

		if(waux_timeout)
        begin
        	 rstate                 <= ST_INIT;
        	 r_FM_OCP_AUX_PWR_EN    <= LOW;
        	 r_FM_OCP_MAIN_PWR_EN   <= LOW;
        end

        else if(w_ocp_main_en)
        begin
        rstate                 <= ST_MAIN_MODE;
		  r_FM_OCP_AUX_PWR_EN    <= HIGH;
        r_FM_OCP_MAIN_PWR_EN   <= HIGH;
        end

        else if (!w_ocp_aux_en)
        begin
        	 rstate   <= ST_ID_MODE;
			 r_FM_OCP_AUX_PWR_EN      <= LOW;
          r_FM_OCP_MAIN_PWR_EN     <= LOW;
        end

        else if (!w_ocp_int)
        begin 
        	rstate  <= ST_INIT;
		   r_FM_OCP_AUX_PWR_EN      <= LOW;
         r_FM_OCP_MAIN_PWR_EN     <= LOW;
        end
    end
    ST_MAIN_MODE:

    begin
    	r_FM_OCP_AUX_PWR_EN    <= HIGH;
        r_FM_OCP_MAIN_PWR_EN   <= HIGH;
		if(wmain_timeout)
        begin
        	 rstate <= ST_INIT;
        	 r_FM_OCP_AUX_PWR_EN    <= LOW;
        	 r_FM_OCP_MAIN_PWR_EN   <= LOW;
        end
        else if (w_ocp_aux_en)
        begin
        	 rstate <= ST_AUX_MODE;
		    r_FM_OCP_AUX_PWR_EN    <= HIGH;
          r_FM_OCP_MAIN_PWR_EN   <= LOW;
		
        end

        else if(w_ocp_id_en)
        begin
        	 rstate <= ST_ID_MODE;
        r_FM_OCP_AUX_PWR_EN      <= LOW;
        r_FM_OCP_MAIN_PWR_EN     <= LOW;
		
		  end


        else if (!w_ocp_int)
        begin
        	 rstate <= ST_INIT;
        r_FM_OCP_AUX_PWR_EN      <= LOW;
        r_FM_OCP_MAIN_PWR_EN     <= LOW;
		  end
        


    end
    default:
    	begin
    		 rstate <= ST_INIT;
    	  r_FM_OCP_AUX_PWR_EN      <= LOW;
        r_FM_OCP_MAIN_PWR_EN     <= LOW;
		end
endcase

	end

end
////////////////////////////////////////////////////////////////////////////////////



wire w_ocp_aux_en_dly_50ms;
wire w_ocp_main_en_dly_50ms;




Signalvalidationdelay#
(
.VALUE                   ( 1'b1 ),
.TOTAL_BITS              ( 3'd6 ),
.POL                     ( 1'b1 )
)SignalValidationDelay_U1         
(           
.i_Clk                   ( iClk ),
.i_Rst                   ( ~iRst_n ),
.i_CE                    ( i_1mSCE ),
.i_vMaxCnt               ( 6'd50 ),       // 50ms
.i_Start                 ( o_FM_OCP_AUX_PWR_EN),
.o_Done                  ( w_ocp_aux_en_dly_50ms )
);

Signalvalidationdelay#
(
.VALUE                   ( 1'b1 ),
.TOTAL_BITS              ( 3'd6 ),
.POL                     ( 1'b1 )
)SignalValidationDelay_U2         
(           
.i_Clk                   ( iClk ),
.i_Rst                   ( ~iRst_n ),
.i_CE                    ( i_1mSCE ),
.i_vMaxCnt               ( 6'd50 ),       // 1S
.i_Start                 ( o_FM_OCP_MAIN_PWR_EN),
.o_Done                  ( w_ocp_main_en_dly_50ms )
);


////////////////////////////////////////////////////////////////////////////////// //

assign     	waux_timeout = r_FM_OCP_AUX_PWR_EN & w_ocp_aux_en_dly_50ms & !i_PWRGD_NIC_PWR;
assign		wmain_timeout = r_FM_OCP_MAIN_PWR_EN & w_ocp_main_en_dly_50ms & !i_PWRGD_NIC_PWR;

////////////////////////////////////////////////////////////////////////////////// //

wire 				w_PWRGD_NIC_PWR_pos;
reg         	r_FM_PLD_OCP_RBT_ISOLATE_N;
assign         o_FM_PLD_OCP_RBT_ISOLATE_N = r_FM_PLD_OCP_RBT_ISOLATE_N;
always@(posedge iClk or negedge iRst_n)
begin
    if(~iRst_n)  
        r_FM_PLD_OCP_RBT_ISOLATE_N  <= LOW;
    else if(~w_ocp_int)
        r_FM_PLD_OCP_RBT_ISOLATE_N  <= LOW;
    else if(w_PWRGD_NIC_PWR_pos)
        r_FM_PLD_OCP_RBT_ISOLATE_N  <= HIGH;
    else
        r_FM_PLD_OCP_RBT_ISOLATE_N  <= r_FM_PLD_OCP_RBT_ISOLATE_N;
end

Edge_Detect#(
.INIT               (HIGH)
)Edge_Detect_U
(
.i_Clk              (iClk),                 //input Clk
.i_Rst_n            (iRst_n),               //Global rst,Active Low
.i_Signal           (i_PWRGD_NIC_PWR),

.O_Signal_Edge      (w_PWRGD_NIC_PWR_pos)    //Output Signal
);
// isolate on the baseboard side when AUX_PWR_EN=0 or when (AUX_PWR_EN=1 and NIC_PWR_GOOD=0) or when (MAIN_PWR_EN=1 and NIC_PWR_GOOD=0),
//Only when aux power or main power ramp up to power good can o_FM_PLD_OCP_RBT_ISOLATE_N=1




///////////////////////////////////////////////////////////////////////////////// //
//Don't want OCP power to affect master power sequence, Only use LED to indicate
reg r_PWRGD_NIC_PWR_DLY_ff;
reg r_OcpPwrFlt;
assign o_OcpPwrFlt_LED                = r_OcpPwrFlt;

always@(posedge iClk or negedge iRst_n)
begin
    if(~iRst_n)
    begin
        r_OcpPwrFlt             <= LOW;
        r_PWRGD_NIC_PWR_DLY_ff  <= LOW;
    end
    else 
    begin
        r_OcpPwrFlt             <= (o_FM_OCP_MAIN_PWR_EN & (~o_PWRGD_NIC_PWR_DLY) & r_PWRGD_NIC_PWR_DLY_ff ) ? HIGH: r_OcpPwrFlt;
        r_PWRGD_NIC_PWR_DLY_ff  <= o_PWRGD_NIC_PWR_DLY;
    end
end

assign       o_PWRGD_NIC_PWR_DLY = i_PWRGD_NIC_PWR&&w_ocp_main_en_dly_50ms;//indicate that power up sequence finish

endmodule
